package org.latestbit.smartobjects.perftests.client;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import org.latestbit.smartobjects.ISmartBus;
import org.latestbit.smartobjects.coders.IDataTypesCoder;
import org.latestbit.smartobjects.io.ResizableByteBuffer;
import org.latestbit.smartobjects.perftests.IPerfTestService;
import org.latestbit.smartobjects.perftests.ITestChoiceType;
import org.latestbit.smartobjects.perftests.ITestStructureType;
import org.latestbit.smartobjects.perftests.TestChoiceType;
import org.latestbit.smartobjects.perftests.TestStructureType;

public abstract class AbstractPerfTestImpl {
	private AtomicInteger sequenceGenerator = new AtomicInteger(0);
	private final String strPattern = "HelloMyFriendAndWelcomeToWorld";
	// private final String strPattern = "Hello";
	
	protected CommandLineOptions options;
	protected ExecutorService threadPool;
	protected volatile boolean stopFlag = false;
	protected IPerfTestService service;
	protected int packetSize = 0;
	
	public AbstractPerfTestImpl(ISmartBus bus, CommandLineOptions options, IPerfTestService service) {
		this.options = options;
		this.service = service;
		this.threadPool = Executors.newFixedThreadPool(options.getThreadsCount());
		try {
			ResizableByteBuffer buffer = testFillRequest(bus, createTestParamStr(), createTestParamInt(), createTestStructure(), createTestStructureArray(options.getArraySize()));
			buffer.getBuffer().flip();
			this.packetSize  = buffer.getBuffer().limit() + org.latestbit.smartobjects.coders.StdNetworkCommand.HEADER_SIZE;
			System.out.println("Packet: "+org.latestbit.smartobjects.utils.ByteTools.byteArrayToHexString(buffer.getBuffer()));
			System.out.println("Packet size: "+packetSize);
			
			buffer.getBuffer().clear();
			createTestStructure().serialize(bus.createDataTypesCoder(), null, buffer);
			buffer.getBuffer().flip();
			System.out.println("Byte buffer of one array item: "+
					org.latestbit.smartobjects.utils.ByteTools.byteArrayToHexString(buffer.getBuffer()));
			System.out.println("Size of one array item: "+buffer.getBuffer().limit());
			
			System.out.println("Array size: "+options.getArraySize());
		}
		catch(Exception ex) {
			ex.printStackTrace();
		}		
	}

	final protected ITestStructureType createTestStructure() {
		ITestStructureType result = new TestStructureType();
		result.setParamInt(createTestParamInt());
		result.setParamStr(createTestParamStr());
		result.setParamChoice(createTestChoiceType());
		return result; 
	}
	
	final protected ITestChoiceType createTestChoiceType() {
		ITestChoiceType result = new TestChoiceType();
		//result.setParamInt(createTestParamInt());
		result.setParamStr(createTestParamStr());
		return result;
	}
	
	final protected ITestStructureType[] createTestStructureArray(int arraySize) {
		ITestStructureType[] result = new TestStructureType[arraySize];
		for(int i=0; i<arraySize; i++) {
			result[i] = createTestStructure();
		}
		return result;
	}

	final protected String createTestParamStr() {
		return strPattern;
	}

	final protected int createTestParamInt() {
		return sequenceGenerator.addAndGet(1);
	}

	public abstract void start() throws Exception;

	public abstract void stop();
	
	public ResizableByteBuffer testFillRequest(ISmartBus bus, String paramStr ,  int paramInt ,  ITestStructureType paramStruct ,  ITestStructureType[] paramArray  ) throws Exception  {
        ResizableByteBuffer writeBuffer = new ResizableByteBuffer(false, 1024, 65535, 4096);
        IDataTypesCoder coder = bus.createDataTypesCoder();

        coder.encodeString(paramStr, writeBuffer);
        coder.encodeInt32(paramInt, writeBuffer);

        if(paramStruct!=null) {
            coder.encodeObjectType(paramStruct.getObjectType(), writeBuffer);
            paramStruct.serialize(coder, null, writeBuffer);
        }

        else
            coder.encodeNull(writeBuffer);

        if(paramArray!=null) {
                coder.encodeObjectArray(paramArray.length, writeBuffer);

                for(ITestStructureType paramArrayItem: paramArray) {
                    if(paramArrayItem!=null) {
                        coder.encodeObjectType(paramArrayItem.getObjectType(), writeBuffer);
                        paramArrayItem.serialize(coder, null, writeBuffer);
                    }

                    else
                        coder.encodeNull(writeBuffer);
                }
        }
        else
            coder.encodeNull(writeBuffer);		
        return writeBuffer;
	}	
}
